﻿#include "stdafx.h"

#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <iconv.h>
#endif

#include <wchar.h>
#include "AddInNative.h"
#include <string>
#include <vector>

//#include <iostream>
//#include <fstream>
//#include <regex>

//static wchar_t* g_PropNames[]{ L"size", L"speed" };
//static wchar_t* g_PropNamesRu[]{ L"размер", L"скорость" };
//static wchar_t* g_MethodNames[]{ L"findString", L"FindRegex" };
//static wchar_t* g_MethodNamesRu[]{ L"найтиСтроку", L"найтиСРегулярнымВыражением" };

static std::vector<wchar_t*> g_PropNames{ L"Name", L"Version" };
static std::vector<wchar_t*> g_PropNamesRu{ L"Имя", L"Версия" };
static std::vector<wchar_t*> g_MethodNames{ L"ItsAlive", L"GetVersion", L"GetName" };
static std::vector<wchar_t*> g_MethodNamesRu{ L"ОноЖивое", L"ПолучитьВерсию", L"ПолучитьИмя" };

// TODO: Насколько нужна эта константа?
// Почему сразу не инициировать литералом переменную "s_names"
static const WCHAR_T g_kClassNames[] = u"YuraClass"; //|OtherClass1|OtherClass2";

long findName(const std::vector<wchar_t*>&, wchar_t**);
uint32_t convToShortWchar(WCHAR_T** Dest, const wchar_t* Source, size_t len = 0);
uint32_t convFromShortWchar(wchar_t** Dest, const WCHAR_T* Source, uint32_t len = 0);
uint32_t getLenShortWcharStr(const WCHAR_T* Source);
static AppCapabilities g_capabilities = eAppCapabilitiesInvalid;
static std::u16string s_names(g_kClassNames);
//---------------------------------------------------------------------------//
long GetClassObject(const WCHAR_T* wsName, IComponentBase** pInterface)
{
    if(!*pInterface)
    {
        *pInterface= new YuraClass();
        return (long)*pInterface;
    }
    return 0;
}
//---------------------------------------------------------------------------//
AppCapabilities SetPlatformCapabilities(const AppCapabilities capabilities)
{
    g_capabilities = capabilities;
    return eAppCapabilitiesLast;
}
//---------------------------------------------------------------------------//
AttachType GetAttachType()
{
    return eCanAttachAny;
}
//---------------------------------------------------------------------------//
long DestroyObject(IComponentBase** pIntf)
{
    if(!*pIntf)
        return -1;

    delete *pIntf;
    *pIntf = 0;
    return 0;
}
//---------------------------------------------------------------------------//
const WCHAR_T* GetClassNames()
{
    return s_names.c_str();
}
//---------------------------------------------------------------------------//
//CAddInNative
YuraClass::YuraClass()
{
    m_iMemory = nullptr;
    name = std::wstring(L"KYB test native 1c component");
    version = std::wstring(L"0.1.1");
}
//---------------------------------------------------------------------------//
YuraClass::~YuraClass()
{
}
//---------------------------------------------------------------------------//
bool YuraClass::Init(void* pConnection)
{ 
    return true;
}
//---------------------------------------------------------------------------//
long YuraClass::GetInfo()
{ 
    return 2000; 
}
//---------------------------------------------------------------------------//
void YuraClass::Done()
{
}
/////////////////////////////////////////////////////////////////////////////
// ILanguageExtenderBase
//---------------------------------------------------------------------------//
bool YuraClass::RegisterExtensionAs(WCHAR_T** wsExtensionName)
{ 
    if (m_iMemory)
    {
        bool allocRes = m_iMemory->AllocMemory(
            (void**)wsExtensionName,
            static_cast<unsigned>(extensionName.size() * sizeof(WCHAR_T)));

        if (allocRes)
        {
            convToShortWchar(wsExtensionName, extensionName.data(), extensionName.size());
            //**wsExtensionName = *ere;
            return true;
        }
    }
    return false;
}
//---------------------------------------------------------------------------//
long YuraClass::GetNProps()
{ 
    return static_cast<long>(std::size(g_PropNames));
}
//---------------------------------------------------------------------------//
long YuraClass::FindProp(const WCHAR_T* wsPropName)
{ 
    long plPropNum = -1;
    wchar_t* propName = nullptr;

    ::convFromShortWchar(&propName, wsPropName);

    plPropNum = ::findName(g_PropNames, &propName);
    if (plPropNum == -1)
        plPropNum = ::findName(g_PropNamesRu, &propName);

    return plPropNum;
}
//---------------------------------------------------------------------------//
const WCHAR_T* YuraClass::GetPropName(long lPropNum, long lPropAlias)
{ 
    WCHAR_T* wsPropName = nullptr;
    
    if (lPropNum > g_PropNames.size() - 1)
        return wsPropName;

    std::wstring wsCurrentName;

    switch (static_cast<LocaleVariant>(lPropAlias))
    {
        case LocaleVariant::Eng:
            wsCurrentName = g_PropNames.at(lPropNum);
            break;
        case LocaleVariant::Rus:
            wsCurrentName = g_PropNamesRu.at(lPropNum);
            break;
        default:
            return wsPropName;
    }

    if (m_iMemory)
    {
        bool resAllocMemory = m_iMemory->AllocMemory(
                (void**)&wsPropName,
                static_cast<unsigned>(wsCurrentName.size() * sizeof(WCHAR_T)));
        if (!resAllocMemory)
        {
            convToShortWchar(&wsPropName, wsCurrentName.data());
        }
    }

    return wsPropName;
}
//---------------------------------------------------------------------------//
bool YuraClass::GetPropVal(const long lPropNum, tVariant* pvarPropVal)
{ 
    if (lPropNum > g_PropNames.size() - 1)
    {
        pvarPropVal->vt = VTYPE_EMPTY;
        return false;
    }

    Props prop = static_cast<Props>(lPropNum);

    switch (prop)
    {
        case Props::Version:
            if (m_iMemory)
            {
                unsigned stringSize = static_cast<unsigned>(version.size()) + 1;
                if (m_iMemory->AllocMemory((void**)&(pvarPropVal->pwstrVal), stringSize * sizeof(WCHAR_T)))
                {
                    pvarPropVal->vt = VTYPE_PWSTR;
                    memcpy(pvarPropVal->pwstrVal, version.data(), version.size() * sizeof(WCHAR_T));
                    pvarPropVal->wstrLen = version.size();
                    return true;
                }
            }
            else
            {
                pvarPropVal->vt = VTYPE_EMPTY;
                return false;
            }
            break;
        case Props::Name:
            if (m_iMemory)
            {
                unsigned stringSize = static_cast<unsigned>(name.size()) + 1;
                if (m_iMemory->AllocMemory((void**)&(pvarPropVal->pwstrVal), stringSize * sizeof(WCHAR_T)))
                {
                    pvarPropVal->vt = VTYPE_PWSTR;
                    memcpy(pvarPropVal->pwstrVal, name.data(), name.size() * sizeof(WCHAR_T));
                    pvarPropVal->wstrLen = name.size();
                    return true;
                }
            }
            else
            {
                pvarPropVal->vt = VTYPE_EMPTY;
                return false;
            }
            break;
    default:
        pvarPropVal->vt = VTYPE_EMPTY;
        return false;
    }
}
//---------------------------------------------------------------------------//
bool YuraClass::SetPropVal(const long lPropNum, tVariant *varPropVal)
{ 
    if (lPropNum > g_PropNames.size() - 1)
    {
        varPropVal->vt = VTYPE_EMPTY;
        return false;
    }

    wchar_t* vol = NULL;

    Props prop = static_cast<Props>(lPropNum);
    switch (prop)
    {
        case Props::Version:
            convFromShortWchar(&vol, varPropVal->pwstrVal);
            version = std::wstring(vol);
            break;
        case Props::Name:
            convFromShortWchar(&vol, varPropVal->pwstrVal);
            name = std::wstring(vol);
            break;
        default:
            return false;
    }

    return true;

}
//---------------------------------------------------------------------------//
bool YuraClass::IsPropReadable(const long lPropNum)
{ 
    Props props = static_cast<Props>(lPropNum);

    switch (props)
    {
    case Props::Name:
        return true;
        break;
    case Props::Version:
        return true;
        break;
    default:
        return false;
        break;
    }
}
//---------------------------------------------------------------------------//
bool YuraClass::IsPropWritable(const long lPropNum)
{
    bool result{false};
    Props props = static_cast<Props>(lPropNum);

    switch (props)
    {
    case Props::Name:
        result = true;
        break;
    case Props::Version:
        result = true;
        break;
    default:
        result = false;
        break;
    }

    return result;
}
//---------------------------------------------------------------------------//
long YuraClass::GetNMethods()
{ 
    //return static_cast<long>(Methods::eLastMethod);

    return static_cast<long>(g_MethodNames.size());
}
//---------------------------------------------------------------------------//
long YuraClass::FindMethod(const WCHAR_T* wsMethodName)
{ 
    long metodIndex = -1;
    wchar_t* metodName = nullptr;

    ::convFromShortWchar(&metodName, wsMethodName);

    metodIndex = ::findName(g_MethodNames, &metodName);
    if (metodIndex == -1)
    {
        metodIndex = ::findName(g_MethodNamesRu, &metodName);
    }

    return metodIndex;
}
//---------------------------------------------------------------------------//
const WCHAR_T* YuraClass::GetMethodName(const long lMethodNum, const long lMethodAlias)
{ 
    std::wstring methodName;
    WCHAR_T* wcMethodName = nullptr;

    switch (static_cast<LocaleVariant>(lMethodAlias))
    {
        case LocaleVariant::Eng:
            methodName = g_MethodNames.at(lMethodNum);
            break;
        case LocaleVariant::Rus:
            methodName = g_MethodNamesRu.at(lMethodNum);
            break;
        default:
            return nullptr;
    }

    if (m_iMemory)
    {
        bool resAllocMemory = m_iMemory->AllocMemory(
                (void**)&wcMethodName,
                static_cast<unsigned>(methodName.size() * sizeof(WCHAR_T)));
        if (!resAllocMemory)
        {
            convToShortWchar(&wcMethodName, methodName.data());
        }
    }

    return wcMethodName;

}
//---------------------------------------------------------------------------//
long YuraClass::GetNParams(const long lMethodNum)
{ 
    
    long paramNum{};
    auto method = static_cast<Methods>(lMethodNum);

    switch (method)
    {
    case Methods::ItsAlive:
        paramNum = 0;
        break;
    case Methods::GetName:
        paramNum = 0;
        break;
    case Methods::GetVersion:
        paramNum = 0;
        break;
    default:
        break;
    }

    return paramNum;

}
//---------------------------------------------------------------------------//
bool YuraClass::GetParamDefValue(const long lMethodNum, const long lParamNum,
                        tVariant *pvarParamDefValue)
{ 
    return false;
} 
//---------------------------------------------------------------------------//
bool YuraClass::HasRetVal(const long lMethodNum)
{ 
    bool returnVol = false;
    auto method = static_cast<Methods>(lMethodNum);

    switch (method)
    {
    case Methods::ItsAlive:
        returnVol = true;
        break;
    case Methods::GetName:
        returnVol = true;
        break;
    case Methods::GetVersion:
        returnVol = true;
        break;
    default:
        break;
    }

    return returnVol;
}
//---------------------------------------------------------------------------//
bool YuraClass::CallAsProc(const long lMethodNum,
                    tVariant* paParams, const long lSizeArray)
{ 
    return true;
}
//---------------------------------------------------------------------------//
bool YuraClass::CallAsFunc(const long lMethodNum,
                tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray)
{ 

    switch (static_cast<Methods>(lMethodNum))
    {
    case Methods::ItsAlive:
        pvarRetValue->vt = VTYPE_BOOL;
        pvarRetValue->bVal = true;
        break;
    case Methods::GetName:
        if (m_iMemory)
        {
            unsigned stringSize = static_cast<unsigned>(name.size()) + 1;
            if (m_iMemory->AllocMemory((void**)&(pvarRetValue->pwstrVal), stringSize * sizeof(WCHAR_T)))
            {
                pvarRetValue->vt = VTYPE_PWSTR;
                memcpy(pvarRetValue->pwstrVal, name.data(), name.size() * sizeof(WCHAR_T));
                pvarRetValue->wstrLen = name.size();
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            pvarRetValue->vt = VTYPE_EMPTY;
            return false;
        }
        break;
    case Methods::GetVersion:
        if (m_iMemory)
        {
            unsigned stringSize = static_cast<unsigned>(version.size()) + 1;
            if (m_iMemory->AllocMemory((void**)&(pvarRetValue->pwstrVal), stringSize * sizeof(WCHAR_T)))
            {
                pvarRetValue->vt = VTYPE_PWSTR;
                memcpy(pvarRetValue->pwstrVal, version.data(), version.size() * sizeof(WCHAR_T));
                pvarRetValue->wstrLen = version.size();
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            pvarRetValue->vt = VTYPE_EMPTY;
            return false;
        }
        break;
    default:
        return false;
        break;
    }
}
//---------------------------------------------------------------------------//
void YuraClass::SetLocale(const WCHAR_T* loc)
{
}
//---------------------------------------------------------------------------//
void ADDIN_API YuraClass::SetUserInterfaceLanguageCode(const WCHAR_T * lang)
{
}
//---------------------------------------------------------------------------//
bool YuraClass::setMemManager(void* mem)
{
    m_iMemory = (IMemoryManager*)mem;
    return m_iMemory != 0;
}
//---------------------------------------------------------------------------//
uint32_t convToShortWchar(WCHAR_T** Dest, const wchar_t* Source, size_t len)
{
    if (!len)
        len = ::wcslen(Source) + 1;

    if (!*Dest)
        *Dest = new WCHAR_T[len];

    WCHAR_T* tmpShort = *Dest;
    wchar_t* tmpWChar = (wchar_t*) Source;
    uint32_t res = 0;

    ::memset(*Dest, 0, len * sizeof(WCHAR_T));

#if defined( __linux__ ) || defined(__APPLE__)
    size_t succeed = (size_t)-1;
    size_t f = len * sizeof(wchar_t), t = len * sizeof(WCHAR_T);
    const char* fromCode = sizeof(wchar_t) == 2 ? "UTF-16" : "UTF-32";
    iconv_t cd = iconv_open("UTF-16LE", fromCode);
    if (cd != (iconv_t)-1)
    {
        succeed = iconv(cd, (char**)&tmpWChar, &f, (char**)&tmpShort, &t);
        iconv_close(cd);
        if(succeed != (size_t)-1)
            return (uint32_t)succeed;
    }
#endif 
    for (; len; --len, ++res, ++tmpWChar, ++tmpShort)
    {
        *tmpShort = (WCHAR_T)*tmpWChar;
    }

    return res;
}
//---------------------------------------------------------------------------//
long findName(
    const std::vector<wchar_t*>& names,
    wchar_t** name)
{
    long ret = -1;
    for (int i = 0; i < names.size(); i++)
    {
        // if (*(names[i]) == **name)
        if (!wcscmp(names[i], *name))
        {
            ret = i;
            break;
        }
    }
    return ret;
}
//---------------------------------------------------------------------------//
uint32_t convFromShortWchar(wchar_t** Dest, const WCHAR_T* Source, uint32_t len)
{
    if (!len)
        len = getLenShortWcharStr(Source) + 1;

    if (!*Dest)
        *Dest = new wchar_t[len];

    wchar_t* tmpWChar = *Dest;
    WCHAR_T* tmpShort = (WCHAR_T*)Source;
    uint32_t res = 0;

    ::memset(*Dest, 0, len * sizeof(wchar_t));
#if defined( __linux__ ) || defined(__APPLE__)
    size_t succeed = (size_t)-1;
    const char* fromCode = sizeof(wchar_t) == 2 ? "UTF-16" : "UTF-32";
    size_t f = len * sizeof(WCHAR_T), t = len * sizeof(wchar_t);
    iconv_t cd = iconv_open("UTF-32LE", fromCode);
    if (cd != (iconv_t)-1)
    {
        succeed = iconv(cd, (char**)&tmpShort, &f, (char**)&tmpWChar, &t);
        iconv_close(cd);
        if(succeed != (size_t)-1)
            return (uint32_t)succeed;
    }
#endif 
    for (; len; --len, ++res, ++tmpWChar, ++tmpShort)
    {
        *tmpWChar = (wchar_t)*tmpShort;
    }

    return res;
}
//---------------------------------------------------------------------------//
uint32_t getLenShortWcharStr(const WCHAR_T* Source)
{
    uint32_t res = 0;
    WCHAR_T *tmpShort = (WCHAR_T*)Source;

    while (*tmpShort++)
        ++res;

    return res;
}
//---------------------------------------------------------------------------//

//wchar_t getTypeStringToWchar(const std::string* Data)
//{
//    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
//    std::wstring wideString = converter.from_bytes(*Data);
//    // Теперь wideString содержит значение типа wchar_t
//    return wideString.data();
//}
//---------------------------------------------------------------------------//